package runner;

import java.net.InetAddress;
import java.util.Hashtable;

import server.MultiThreadedServer;

public class Message {

	public static final int MESSAGE_PARTS = 8;
	public static final int MESSAGE_EXCEPTION = 0;

	private String code;
	private String source;
	private String sourceIP; 
	private int sourcePort; 
	private String destinationIP; 
	private int destinationPort; 
	private String destination;
	private String key;
	private String value;
	private long timestamp;
	private int TTL;
	private String msg_machine;

	private void setSourceIP(String iPAddress) {
		this.sourceIP = iPAddress;
	}
	
	private void setSourcePort(int port) {
		this.sourcePort = port; 
	}
	
	public String getSourceIP() {
		return this.sourceIP; 
	}
	
	public int getSourcePort() {
		return this.sourcePort; 
	}
	
	private void setDestinationIP(String iPAddress) {
		this.destinationIP = iPAddress;
	}
	
	private void setDestinationPort(int port) {
		this.destinationPort = port; 
	}
	
	public String getDestinationIP() {
		return this.destinationIP; 
	}
	
	public int getDestinationPort() {
		return this.destinationPort; 
	}

	public String getCode() {
		return code;
	}

	private String getSource() {
		return source;
	}

	private String getDestination() {
		return destination;
	}

	private String getKey() {
		return key;
	}

	private String getValue() {
		return value;
	}

	private long getTimestamp() {
		return timestamp;
	}

	private int getTTL() {
		return TTL;
	}

	private String getMsg_machine() {
		return msg_machine;
	}

	private void setCode(String code) {
		this.code = code;
	}

	private void setSource(String source) {
		setSourceIP(source.split("-")[0]); 
		setSourcePort(Integer.parseInt(source.split("-")[1])); 
		this.source = source;
	}

	public void setDestination(String destination) {
		this.destination = destination;
	}

	private void setKey(String key) {
		this.key = key;
	}

	private void setValue(String value) {
		this.value = value;
	}

	private void setTimestamp(long timestamp) {
		this.timestamp = timestamp;
	}

	private void setTTL(int tTL) {
		TTL = tTL;
	}

	private void setMsg_machine(String msg_machine) {
		this.msg_machine = msg_machine;
	}

	public Message(String inputMessage) {
		String[] msg = inputMessage.split(":");
		if (msg.length != MESSAGE_PARTS) {
			MultiThreadedServer
					.print("Error: Incorrect format of the message received");
			// System.exit(MESSAGE_EXCEPTION);
		} else {
			setCode(msg[0]);
			setSource(msg[1]);
			setDestination(msg[2]);
			setKey(msg[3]);
			setValue(msg[4]);
			setTimestamp(Long.parseLong(msg[5]));
			setTTL(Integer.parseInt(msg[6]));
			setMsg_machine(msg[7]);
		}
	}

	public String createConnectionNewMachineMessage() {
		String str = "WaitForConnection:" + "blank_sourceIP:" + this.getSourceIP() + "-" + this.getSourcePort() + ":blank_key:blank_value:" + System.currentTimeMillis() + ":" + 1 + ":blank_message"; 
		return str; 
	}
	
	public int calculateMinArray(int[] array) {
		int min = Integer.MAX_VALUE;
		for (int i = 0; i < array.length; i++) {
			if (array[i] < min) {
				min = array[i];
			}
		}
		return min;
	}

	public int calculateMinPos(int[] array) {
		int min = Integer.MAX_VALUE;
		int pos = -1;
		for (int i = 0; i < array.length; i++) {
			if (array[i] < min) {
				min = array[i];
				pos = i;
			}
		}
		return pos;
	}

	public String analyzeMessage(Hashtable<Integer, String> hashtable) {
		if (getCode().equalsIgnoreCase("insert")) {
			setTTL(getTTL() - 1);
			setMsg_machine(getMsg_machine() + hashtable.size() + ",");
		} else if (getCode().equalsIgnoreCase("delete")) {
			setTTL(getTTL() - 1);
			if (hashtable.containsKey(getKey())) {
				setDestination(getMyIP());
				setValue("");
				setTimestamp(System.currentTimeMillis() - getTimestamp());
				setMsg_machine("");
			}
		} else if (getCode().equalsIgnoreCase("search")) {
			setTTL(getTTL() - 1);
			if (hashtable.containsKey(getKey())) {
				setDestination(getMyIP());
				setValue(hashtable.get(getKey()));
				setTimestamp(System.currentTimeMillis() - getTimestamp());
				setMsg_machine("");
			}
		} else if (getCode().equalsIgnoreCase("insert-now")) {
			setTTL(getTTL() - 1);
		} else if (getCode().equalsIgnoreCase("machineInsert")) {
			setCode("ConnectRight"); 
			setSource(getMyIP()); 
			setKey("");
			setValue(""); 
			setTimestamp(System.currentTimeMillis()); 
			setTTL(1); 
			setMsg_machine(""); 
		} else if (getCode().equalsIgnoreCase("ConnectRight")) {
			
		} else {
			//dummy function to test. 
			
		}
		
		isMessageDead(hashtable); 
		
		return formMessageNow();
	}

	public void isMessageDead(Hashtable<Integer, String> hashtable) {
		if (getTTL() == 0) {
			if (getCode().equalsIgnoreCase("search")
					|| getCode().equalsIgnoreCase("delete")) {
				if (getDestination().equalsIgnoreCase("")) {
					MultiThreadedServer
							.print("Key|Value pair not found in the distributed system. Could not "
									+ getCode());
				} else {
					MultiThreadedServer
							.print("Key|Value pair was found in the distributed system at machine: "
									+ getDestination()
									+ "and key was "
									+ getCode() + "ed");
				}
			} else if (getCode().equalsIgnoreCase("insert")) {
				String[] parsedMessage = getMsg_machine().split(",");
				int[] loadingPerMachine = new int[parsedMessage.length];
				int pos = 0;
				for (String string : parsedMessage) {
					loadingPerMachine[pos++] = Integer.parseInt(string);
				}
				int positionMin = calculateMinPos(loadingPerMachine);
				if (positionMin == 0) {
					hashtable.put(Integer.parseInt(getKey()), getValue());
					MultiThreadedServer
							.print("Key|Value pair inserted in this machine. Pair is "
									+ getKey() + "|" + getValue());
					clearMessage();
				} else {
					setTTL(positionMin);
					setCode("insert-now");
					setDestination("");
					setMsg_machine("");
				}
			} else if (getCode().equalsIgnoreCase("insert-now")) {
				hashtable.put(Integer.parseInt(getKey()), getValue());
				MultiThreadedServer
						.print("Key|Value pair inserted in this machine. Pair is "
								+ getKey() + "|" + getValue());
				clearMessage();
			}
		}
	}
	
	public static String getMyIP() {
		String myIPString = ""; 
		try {
			InetAddress ownIP = InetAddress.getLocalHost();
			myIPString = ownIP.getHostAddress(); 
			System.out.println("IP of my system is := "
					+ ownIP.getHostAddress());
		} catch (Exception e) {
			System.out.println("Exception caught =" + e.getMessage());
		}
		return myIPString; 
	}

	public void clearMessage() {
		setCode("");
		setSource("");
		setDestination("");
		setKey("");
		setValue("");
		setTimestamp(0);
		setTTL(0);
		setMsg_machine("");
	}

	public String formMessageNow() {
		String message = "";
		message += getCode() + ":";
		message += getSource() + ":";
		message += getDestination() + ":";
		message += getKey() + ":";
		message += getValue() + ":";
		message += getTimestamp() + ":";
		message += getTTL() + ":";
		message += getMsg_machine() + ":";

		return message;
	}
}
